package org.ccay.registry;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.ccay.core.dispatcher.EventUtil;
import org.ccay.core.dispatcher.IOrder;
import org.ccay.core.exception.ApplicationException;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.registry.IRegistryManager;
import org.ccay.core.registry.RegistryItem;
import org.ccay.core.request.Application;
import org.ccay.registry.dao.IRegistryDao;
import org.ccay.registry.events.IRegistryChangedHandler;
import org.ccay.registry.events.RegistryChangedEventArgs;
import org.ccay.registry.exception.RegistryNotFindException;
import org.ccay.registry.loader.FileLoader;
import org.ccay.registry.loader.IRegistryLoader;

public class RegistryManager implements IRegistryManager,IRegistryChangedHandler,IOrder{
	
	private static final ILogger logger = CcayLoggerFactory.getLogger(RegistryManager.class);
	
	
	/**
	 * 文件加载及解析器（总是最先执行）
	 */
	private IRegistryLoader fileLoader = new FileLoader();
	
	/**
	 * 数据库加载器 
	 */
	private DBLoader dbLoader = new DBLoader();
	
	/**
	 * registry持久化服务类
	 */
	private IRegistryDao registryDao;
	

	
	private class DBLoader implements IRegistryLoader{
		@Override
		public Map<String, RegistryItem> load() {
			Map<String,RegistryItem> result = new HashMap<String,RegistryItem>();
			List<RegistryItem> dbRegs = registryDao.findAll(Application.getCurrent().getAppName());
			for(RegistryItem item : dbRegs){
				result.put(item.getName(), item);
			}
			return result;
		}
	}
	
	
	public void setRegistryDao(IRegistryDao registryDao) {
		this.registryDao = registryDao;
	}


	public void setFileLoader(IRegistryLoader fileLoader) {
		this.fileLoader = fileLoader;
	}

	/**
	 * 可扩展的其他加载器，可通其他方式加载注册项，当名称相同时值会被覆盖（如果注册项允许覆盖）以支持非停机更新项
	 */
	private List<IRegistryLoader> extLoaders = new ArrayList<IRegistryLoader>();
	
	/**
	 * 存储从文件加载的注册项
	 */
	private Map<String,RegistryItem> fileRegistroyContainer = new ConcurrentHashMap<String,RegistryItem>();
	
	public Map<String, RegistryItem> getFileRegistroyContainer() {
		return fileRegistroyContainer;
	}

	/**
	 * 存储扩展加载的注册项
	 */
	private Map<String,RegistryItem> extRegistroyContainer= new ConcurrentHashMap<String,RegistryItem>();

	public Map<String, RegistryItem> getExtRegistroyContainer() {
		return extRegistroyContainer;
	}

	public void load() {
		logger.info("Loading Registry ...");
		long time = System.currentTimeMillis();
		//加载配置的注册项
		fileRegistroyContainer.putAll(fileLoader.load());
		//加载数据库注册项
		extRegistroyContainer.putAll(dbLoader.load());
		//加载其他加载器的注册项
		for(IRegistryLoader loader : extLoaders){
			extRegistroyContainer.putAll(loader.load());
		}
		logger.info("loaded "+(fileRegistroyContainer.size()+extRegistroyContainer.size())+" registroy items, costed "+(System.currentTimeMillis() - time)+" ms");
	}
	
	@Override
	public boolean hasRegistryItem(String name) {
		return null!=fileRegistroyContainer.get(name) || null!=extRegistroyContainer.get(name);
	}
	
	@Override
	public RegistryItem get(String name) {
		return getItem(name,false);
	}
	
	private RegistryItem getItem(String name,boolean ignoreException) {
		RegistryItem fileRegistItem = fileRegistroyContainer.get(name);
		if(null != fileRegistItem){
			//不是空，并且允许覆盖，则尝试从扩展加载中取值
			if(fileRegistItem.isOverwritable()){
				RegistryItem extRegistItem = extRegistroyContainer.get(name);
				if(null != extRegistItem){//允许覆盖且已覆盖
					RegistryItem newRegItem = new RegistryItem(
							name,
							extRegistItem.getValue(),
							fileRegistItem.getDesc(),
							true);
					return newRegItem;
				}else{//允许覆盖但没有覆盖
					return fileRegistItem;
				}
			}else{//不允许覆盖
				return fileRegistItem;
			}
		}else{//配置中没有，从扩展中取
			RegistryItem extRegistItem = extRegistroyContainer.get(name);
			if(null == extRegistItem && !ignoreException){
				throw new RegistryNotFindException(name);
			}
			return extRegistItem;
		}
	}
	public Integer getIntegerValue(String name) {
		return Integer.parseInt(getValue(name));
	}
	
	public String getValue(String name) {
		return get(name).getValue();
	}

	public Double getDoubleValue(String name){
		return Double.parseDouble(getValue(name));
	}
	
	public Long getLongValue(String name) {
		return Long.parseLong(getValue(name));
	}

	public Boolean getBooleanValue(String name) {
		String value = getValue(name);
		if(value.matches("^\\d+$")){
			//兼容C语言风格的boolean表示方式
			if(Integer.parseInt(value) == 0){
				return false;
			}else{
				return true;
			}
		}else{
			return Boolean.parseBoolean(getValue(name));
		}
	}

	public Date getDateValue(String name, DateFormat dataformat) throws ParseException {
		return dataformat.parse(getValue(name));
	}

	@Override
	public void reload() {
		logger.info("Reloading Registry ...");
		long time = System.currentTimeMillis();
		if(Application.getCurrent().getEnviorment().equals(Application.DEV)){
			//开发环境下 重新装载从文件加载的注册项
			fileRegistroyContainer.clear();
			fileRegistroyContainer.putAll(fileLoader.load());
		}
		extRegistroyContainer.clear();
		//加载数据库注册项
		extRegistroyContainer.putAll(dbLoader.load());
		//加载其他加载器的注册项
		for(IRegistryLoader loader : extLoaders){
			extRegistroyContainer.putAll(loader.load());
		}
		logger.info("Registry loaded. costed "+(System.currentTimeMillis() - time)+"MS");
	}

	@Override
	public void clear() {
		fileRegistroyContainer.clear();
		extRegistroyContainer.clear();
	}

	@Override
	public List<RegistryItem> getAll() {
		Set<String> nameSet = new HashSet<String>();
		nameSet.addAll(fileRegistroyContainer.keySet());
		nameSet.addAll(extRegistroyContainer.keySet());
		
		List<String> nameList = new LinkedList<String>(nameSet);
		Collections.sort(nameList);
		
		List<RegistryItem> result = new ArrayList<RegistryItem>();
		
		for(String name : nameList){
			result.add(this.get(name));
		}
		return result;
	}

	@Override
	public void createOrUpdateRegistryItem(RegistryItem item) throws ApplicationException{
		String name = item.getName();
		RegistryItem fileItem = fileRegistroyContainer.get(name);
		if(null != fileItem ){
			if(fileItem.getValue().equals(item.getValue())){
				//要创建的加载项已在jar包中存在，且值相同，忽略,并删除数据库中已存在的值
				logger.info("Ignore Registry Item create request which has same name["
						+name+"] and value["+item.getValue()+"] already exists in *.registry.xml,"
								+ "convert the requisition as a delete request!.");
				registryDao.deleteRegistry(name,Application.getCurrent().getAppName());
			}else{
				registryDao.deleteRegistry(name,Application.getCurrent().getAppName());
				registryDao.insertRegistry(item,Application.getCurrent().getAppName());
			}
		}else{
			registryDao.deleteRegistry(name,Application.getCurrent().getAppName());
			registryDao.insertRegistry(item,Application.getCurrent().getAppName());
		}
		//发送item changed事件
		EventUtil.dispatch(IRegistryChangedHandler.class, this, 
				new RegistryChangedEventArgs(name), false);
	}

	@Override
	public void deleteRegistryItem(String name) throws ApplicationException {
		registryDao.deleteRegistry(name,Application.getCurrent().getAppName());
		//发送item changed事件
		EventUtil.dispatch(IRegistryChangedHandler.class, 
			this, new RegistryChangedEventArgs(name), false);
	}

	@Override
	public void reload(String registryItemName) {
		RegistryItem item = registryDao.findRegistry(registryItemName,Application.getCurrent().getAppName());
		if(item != null){
			extRegistroyContainer.put(registryItemName, item);
		}else{
			extRegistroyContainer.remove(registryItemName);
		}
	}
	
	@Override
	public void execute(Object sender, RegistryChangedEventArgs args) throws ApplicationException {
		reload(args.getRegistryItemName());
	}

	@Override
	public int getOrder() {
		//管理器总是最先执行注册项修改事件
		return 0;
	}
	
}
